package tech.xiaozai.rbac.security.config;

import com.google.common.collect.ImmutableList;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import tech.xiaozai.rbac.security.JWTWorker;
import tech.xiaozai.rbac.security.LoadPermissionAfterIOCRunning;
import tech.xiaozai.rbac.security.MyMD5PasswordEncoder;
import tech.xiaozai.rbac.security.authentication.*;
import tech.xiaozai.rbac.security.authorization.MyAccessDeniedHandler;
import tech.xiaozai.rbac.security.authorization.MyAuthenticationEndPoint;


/**
 * @author xiaozai
 * @version 1.0
 * @date 2020-09-23 11:38
 * <p>
 * 安全框架的配置
 */

@EnableGlobalMethodSecurity(prePostEnabled = true,jsr250Enabled = true,securedEnabled = true)
@EnableWebSecurity
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {




    @Override
    protected void configure(HttpSecurity http) throws Exception {

//        http.headers().httpStrictTransportSecurity()
//                .maxAgeInSeconds(0)
//                .includeSubDomains(true);
        //csrf是防伪造攻击，如果开启则我们没办法用postman来测试,必须关掉
        http.csrf().disable()
                .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .antMatchers(HttpMethod.POST, "/login").permitAll()
                .antMatchers(HttpMethod.POST,"/logout").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().accessDeniedHandler(myAccessDeniedHandler())
                .authenticationEntryPoint(myAuthenticationEndPoint()).and()
                .logout().logoutUrl("/logout").logoutSuccessHandler(myLogoutSuccessHandler()).and()
                //禁掉session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .cors()
                .and().addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(jwtAuthenticationFilter(), LogoutFilter.class);

    }

    @Bean
    public JWTWorker jwtWorker() {
        return new JWTWorker();
    }


    @Bean
    public LoadPermissionAfterIOCRunning loadPermissionAfterIOCRunning(){

        return new LoadPermissionAfterIOCRunning();
    }


    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter(){
        return new JwtAuthenticationFilter();
    }


    /**
     * 配一个过滤器bean
     *
     * @return
     */
    @Bean
    public MyUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
        MyUsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter("/login");
        filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler());
        filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler());
        filter.setAuthenticationManager(authenticationManager());
        return filter;
    }

    @Bean
    public MyAuthenticationFailureHandler myAuthenticationFailureHandler() {
        return new MyAuthenticationFailureHandler();
    }

    @Bean
    public MyAuthenticationSuccessHandler myAuthenticationSuccessHandler() {
        return new MyAuthenticationSuccessHandler();
    }

    @Bean
    public MyMD5PasswordEncoder myMD5PasswordEncoder() {
        return new MyMD5PasswordEncoder();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new MyUserDetailsService();
    }


    @Bean
    public MyAccessDeniedHandler myAccessDeniedHandler(){
        return new MyAccessDeniedHandler();
    }

    @Bean
    public MyAuthenticationEndPoint myAuthenticationEndPoint(){
        return new MyAuthenticationEndPoint();
    }


    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(ImmutableList.of("*"));
        configuration.setAllowedMethods(ImmutableList.of("HEAD",
                "GET", "POST", "PUT", "DELETE", "PATCH"));
        // setAllowCredentials(true) is important, otherwise:
        // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
        configuration.setAllowCredentials(true);
        // setAllowedHeaders is important! Without it, OPTIONS preflight request
        // will fail with 403 Invalid CORS request
        configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public MyLogoutSuccessHandler myLogoutSuccessHandler(){
        return new MyLogoutSuccessHandler();
    }

}
